package com.viknix.threadpool.manager.client.factory;

import cn.hutool.core.collection.CollUtil;
import com.viknix.threadpool.manager.client.enums.BlockingQueueEnum;
import com.viknix.threadpool.manager.client.exception.TpmProcessException;
import com.viknix.threadpool.manager.client.spi.CustomBlockingQueue;
import com.viknix.threadpool.manager.client.spi.TpServiceLoader;
import org.apache.commons.lang3.StringUtils;

import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.*;

import static com.viknix.threadpool.manager.client.enums.BlockingQueueEnum.*;

/**
 * @Author: Dongqi
 * @Date: 2021/10/26 15:44
 * @Version 1.0
 * @Description: 阻塞队列工厂
 */
public class BlockingQueueFactory {

    /**
     * 根据 code 和 capacity 生产 BlockingQueue，具体 code 选项参见
     *
     * @param code     阻塞队列code
     * @param capacity 阻塞队列容量，如果不是ArrayBlockingQueue，非必传
     * @return 阻塞队列
     * @see com.viknix.threadpool.manager.client.enums.BlockingQueueEnum
     * 也可以通过spi机制，实现 CustomBlockingQueue 接口，根据自定义的code生产自定义阻塞队列
     * @see com.viknix.threadpool.manager.client.spi.CustomBlockingQueue
     * 具体 spi 加载机制参见
     * @see com.viknix.threadpool.manager.client.spi.TpServiceLoader
     */
    public static BlockingQueue createBlockingQueue(Integer code, Integer capacity) {

        // 如果是 ArrayBlockingQueue 该参数必传！
        if (Objects.equals(code, ARRAY_BLOCKING_QUEUE.getCode())) {
            if (Objects.isNull(capacity)) {
                throw new IllegalArgumentException("The capacity parameter cannot be empty!");
            }
        }
        // 默认 capacity 为 2^31-1
        capacity = capacity == null ? Integer.MAX_VALUE : capacity;
        // 先从公共的阻塞队列选项中判断是否有符合条件的 blockingQueue
        BlockingQueue blockingQueue = null;
        if (Objects.equals(code, ARRAY_BLOCKING_QUEUE.getCode())) {
            blockingQueue = new ArrayBlockingQueue(capacity);
        } else if (Objects.equals(code, LINKED_BLOCKING_QUEUE.getCode())) {
            blockingQueue = new LinkedBlockingQueue(capacity);
        } else if (Objects.equals(code, LINKED_BLOCKING_DEQUE.getCode())) {
            blockingQueue = new LinkedBlockingDeque(capacity);
        } else if (Objects.equals(code, DELAY_QUEUE.getCode())) {
            blockingQueue = new DelayQueue();
        } else if (Objects.equals(code, LINKED_TRANSFER_QUEUE.getCode())) {
            blockingQueue = new LinkedTransferQueue();
        } else if (Objects.equals(code, PRIORITY_BLOCKING_QUEUE.getCode())) {
            blockingQueue = new PriorityBlockingQueue(capacity);
        } else if (Objects.equals(code, SYNCHRONOUS_QUEUE.getCode())) {
            blockingQueue = new SynchronousQueue();
        }
        // 如果公共的阻塞队列选项中有符合条件的阻塞队列直接返回
        if (blockingQueue != null) {
            return blockingQueue;
        }
        // 如果公共的阻塞队列中没有符合条件的阻塞队列，
        // 从自定义的阻塞队列中查询是否有符合条件的阻塞队列
        Collection<CustomBlockingQueue> customBlockingQueues = TpServiceLoader.getSingletonServiceInstances(CustomBlockingQueue.class);
        if (CollUtil.isEmpty(customBlockingQueues)) {
            throw new TpmProcessException("Blocking queue production is abnormal!");
        }
        blockingQueue = customBlockingQueues.stream()
                .filter(each -> Objects.equals(code, each.getCode()))
                .map(each -> each.generateBlockingQueue())
                .findFirst()
                .orElseThrow(() -> new TpmProcessException("Blocking queue production is abnormal!"));
        return blockingQueue;
    }

    public static String getNameByCode(Integer code) {

        String name = BlockingQueueEnum.getNameByCode(code);
        if (StringUtils.isNotBlank(name)) {
            return name;
        }
        Collection<CustomBlockingQueue> customBlockingQueues = TpServiceLoader.getSingletonServiceInstances(CustomBlockingQueue.class);
        if (CollUtil.isEmpty(customBlockingQueues)) {
            throw new TpmProcessException("Blocking queue production is abnormal!");
        }
        name = customBlockingQueues.stream()
                .filter(each -> Objects.equals(code, each.getCode()))
                .map(each -> each.getName())
                .findFirst()
                .orElseThrow(() -> new TpmProcessException("Blocking queue production is abnormal!"));
        return name;
    }
}
